﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSE.Framework.Communication.Configuration.Adapters;
using PSE.Framework.Communication.Messages;
using PSE.Framework.Communication.Configuration.Exceptions;

namespace PSE.Framework.Communication.Adapters
{
    /// <summary>
    /// Classe base dos adapters de entrada
    /// </summary>
    /// <typeparam name="TConfig">Tipo da configuração do adapter</typeparam>
    /// <typeparam name="TOpenParameter">Tipo do parâmetro de abertura do adapter</typeparam>
    public abstract class BaseInputAdapter<TConfig, TOpenParameter> : Adapter<TConfig, TOpenParameter>, IBaseInputAdapter
        where TConfig : InputAdapterConfiguration
        where TOpenParameter : class
    {
        #region Construtores
        /// <summary>
        /// Construtor padrão
        /// </summary>
        public BaseInputAdapter()
            : base(AdapterDirection.Input)
        {

        }
        #endregion

        #region Open
        /// <summary>
        /// Abre o adapter de entrada
        /// </summary>
        /// <param name="config">Configuração do adapter</param>
        /// <param name="parameter">Parâmetro de abertura do adapter</param>
        public override void Open(TConfig config, TOpenParameter parameter)
        {
            if (config == null)
                throw new ArgumentNullException("config");

            this.VerifyIsCreated();

            // Criando identifier
            if (config.MessageIdentifier != null)
            {
                try
                {
                    if (!typeof(IMessageIdentifier).IsAssignableFrom(config.MessageIdentifier))
                        throw new InvalidAdapterConfigurationException("Invalid message identifier type, the type must implement IMessageIdentifier", config.Name);

                    this.Identifier = (IMessageIdentifier)Activator.CreateInstance(config.MessageIdentifier);
                }
                catch (Exception ex)
                {
                    throw new AdapterException("Cannot create message identifier", config.Name, AdapterDirection.Input, ex);
                }
            }

            base.Open(config, parameter);
        }

        /// <summary>
        /// Abre o adapter de entrada
        /// </summary>
        /// <param name="config">Configuração do adapter</param>
        void IBaseInputAdapter.Open(InputAdapterConfiguration config)
        {
            this.Open((TConfig)config);
        }

        /// <summary>
        /// Abre o adapter de entrada
        /// </summary>
        /// <param name="config">Configuração do adapter</param>
        /// <param name="parameter">Parâmetro de abertura do adapter</param>
        void IBaseInputAdapter.Open(InputAdapterConfiguration config, object parameter)
        {
            this.Open((TConfig)config, (TOpenParameter)parameter);
        }
        #endregion

        #region Identifier
        private IMessageIdentifier _identifier = null;
        /// <summary>
        /// Identificador de mensagens do adapter
        /// </summary>
        protected IMessageIdentifier Identifier
        {
            get { this.VerifyIsOpened(); return _identifier; }
            set { _identifier = value; }
        }
        #endregion

        #region Identify
        /// <summary>
        /// Identifica uma mensagem e retorna o seu action correspondente
        /// </summary>
        /// <param name="message">Mensagem a ser identificada</param>
        /// <returns>Action da mensagem</returns>
        protected virtual string Identify(object message)
        {
            if (message == null)
                throw new ArgumentNullException("message");

            this.VerifyIsOpened();

            string action = null;

            try
            {
                action = this.Identifier.Identify(message);
            }
            catch (Exception ex)
            {
                throw new AdapterMessageException("Cannot identify the message", this.Configuration.Name, this.AdapterDirection, message, ex);
            }

            ////se ao receber uma mensagem desconhecida o comportamento configurado for disparar um erro
            //if (this.Configuration.UnknowMessageReceivedBehavior == UnknowMessageReceivedBehavior.ThrowException)
            //{
            //    // Verifica se a action retornada não está vazia
            //    if (string.IsNullOrEmpty(action))
            //        throw new AdapterMessageException("Cannot identify the messagem, the action cannot be null", this.Configuration.Name, this.AdapterDirection, message);
            //    // Verifica se a action retornada é tratada pelo adapter
            //    if (!this.Configuration.Actions.Contains(action))
            //    {
            //        // Não existe no processo
            //        throw new AdapterMessageException("Cannot identify the messagem, the action doesn't exist in process", this.Configuration.Name, this.AdapterDirection, action, message);
            //    }
            //}

            return action;
        }
        #endregion

        #region Deserialize
        /// <summary>
        /// Deserializa a mensagem do formato original para um objeto
        /// </summary>
        /// <param name="message">Mensagem no formato original</param>
        /// <param name="action">Action da mensagem</param>
        /// <returns>Mensagem no formato de objeto</returns>
        protected abstract object Deserialize(object message, string action);
        #endregion

        #region Close
        /// <summary>
        /// Fecha o adapter de entrada
        /// </summary>
        public override void Close()
        {
            this.VerifyIsOpened();

            base.Close();
        }
        #endregion

        #region ProcessAndDeliveryMessage
        /// <summary>
        /// Recebe a mensagem no formato original, Processa(identifica, deserializa ,converte) e entrega a mensagem
        /// </summary>
        /// <param name="message">Mensagem no formato original</param>
        /// <returns>retorna a mensagem tratada de acordo com as configurações do provider</returns>
        protected virtual MessageInfo ProcessAndDeliveryMessage(object message)
        {
            if (message == null)
                throw new ArgumentNullException("message");

            this.VerifyIsOpened();

            string action = null;
            object deserializedMessage = null;
            //object convertedMessage = null;


            // Identificando mensagem
            // ------------------------------------------------------------------------------------
            if (this.Identifier != null)
            {
                action = this.Identify(message);
            }
            else
            {
                // Se não existir um MessageIdentifier configurado, utiliza a primeira 
                // action do adapter
                action = this.Configuration.Actions[0].Name;
            }

            //se nao foi encontrada uma action para a mensagem, ou seja, trata-se de uma mensagem desconhecida
            if (String.IsNullOrEmpty(action) || !this.Configuration.Actions.Contains(action))
            {
                switch (this.Configuration.UnknowMessageReceivedBehavior)
                {
                    //receber a mensagem serializada
                    case UnknowMessageReceivedBehavior.ReceiveSerializedMessage:
                        action = string.Empty;
                        deserializedMessage = message;
                        break;
                    //ignorar a mensagem
                    case UnknowMessageReceivedBehavior.IgnoreMessage:
                        return null;
                    //disparar erro 
                    case UnknowMessageReceivedBehavior.ThrowException:
                        throw new AdapterMessageException("Unknow message received. If you want to receice or ignore this message, please configure the property 'unknowMessageReceivedBehavior' with the values 'ReceiveSerializedMessage' or 'IgnoreMessage'",
                                                            this.Configuration.Name, AdapterDirection.Input, message);
                        break;
                }
            }
            else
            {

                // Deserializando a mensagem
                // ------------------------------------------------------------------------------------
                try
                {
                    deserializedMessage = this.Deserialize(message, action);
                }
                catch (Exception ex)
                {
                    throw new AdapterMessageException("Cannot deserialize the message", this.Configuration.Name, AdapterDirection.Input, action, message, ex);
                }

                // Verifica se a mensagem deserializada não é nula
                if (deserializedMessage == null)
                    throw new AdapterMessageException("Cannot deserialize the message, the deserialized message cannot be null", this.Configuration.Name, AdapterDirection.Input, action, message);


                //// Convertendo a mensagem
                //// ------------------------------------------------------------------------------------
                //convertedMessage = this.Convert(deserializedMessage, action);
            }

            // Retornar um objeto MessageInfo, contendo a Action e a Mensagem propriamente dita
            return new MessageInfo(action, deserializedMessage);
        }
        #endregion
    }
}
